`include "defines.v"
`include "inst.v"
module controlStatusRegisiter(
  input clk,
  input rst_n,
  // 同步写端口
  input                 wen,
  input  [11:0]         waddr,
  input  [`XLEN-1:0]    wdata,
  // 读端口
  input  [11:0]         raddr,
  output [`XLEN-1:0]    rdata,
  // wbu in
  input                 wbu_csr_commit_i,
  input  [`EXCEPT:0]    wbu_csr_exception_i,
  input  [`VADDR_W-1:0] wbu_csr_pc_i,
  input  [31:0]         wbu_csr_inst_i,
  // clint interrupt
  output                clintEn,
  input                 clintInterrupt,
  // commit flush instr queue
  output                commit_flush,
  output [`VADDR_W-1:0] commit_addr ,
  output                commit_invalidate,
  output                csr_wbu_isClint_o
);
wire [`XLEN-1:0] mstatus  ; //
reg  [`XLEN-1:0] misa     ; // 
reg  [`XLEN-1:0] medeleg  ;
reg  [`XLEN-1:0] mideleg  ;
wire [`XLEN-1:0] mie      ; // 
reg  [`XLEN-1:0] mtvec    ; //
reg  [`XLEN-1:0] mscratch ; //
reg  [`XLEN-1:0] mepc     ; //
reg  [`XLEN-1:0] mcause   ; //
reg  [`XLEN-1:0] mtval    ;
wire [`XLEN-1:0] mip      ; //
reg  [`XLEN-1:0] mcycle   ; //
reg  [`XLEN-1:0] minstret ; // 
reg  [`XLEN-1:0] marchid  ; // 
reg  [`XLEN-1:0] mimpid   ;
// mstatus
reg SD;reg [1:0] FS; reg [1:0] PRV; reg MIE;reg [1:0] MPP;reg MPIE;
//mip
reg MTIP;
//mie
reg MTIE;
wire isMret        = wbu_csr_commit_i && wbu_csr_exception_i[`mret]         ;
wire isEcall       = wbu_csr_commit_i && wbu_csr_exception_i[`ecall]        ;
wire isClint       = wbu_csr_commit_i && MTIE && MTIP && MIE ;
// wire isClint       = wbu_csr_commit_i && wbu_csr_exception_i[`clint] && MIE ;
wire isilegalInstr = wbu_csr_commit_i && wbu_csr_exception_i[`ilginst]      ;
wire isFenceI      = wbu_csr_commit_i && wbu_csr_exception_i[`fencei]       ;

assign commit_invalidate = isFenceI;
assign commit_flush  = isEcall || isMret || isClint || isFenceI;
assign commit_addr   = isFenceI ? wbu_csr_pc_i + 'd4           :
                       isClint  ? {mtvec[`VADDR_W-1:2],2'd0}   :
                       isEcall  ? {mtvec[`VADDR_W-1:2],2'd0}   :
                       isMret   ? {mepc[`VADDR_W-1:2] ,2'd0}    :
                       `ZERO; 
assign clintEn       = MTIE;
assign csr_wbu_isClint_o = isClint;
// FIND:example
// always@(posedge clk or negedge rst_n)
//   if(~rst_n)
//     t <= `ZERO;
//   else if(wen && (waddr == `CSR_ADDR_xxx))
//     t <= wdata;
// FIND:mtval
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mtval <= `ZERO;
  else if(wen & (waddr == `CSR_ADDR_mtval))
    mtval <= wdata;
// FIND:mcycle
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mcycle <= `ZERO;
  else 
    mcycle <= mcycle + 1'b1;
// FIND:minstret
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    minstret <= `ZERO;
  else if(wbu_csr_commit_i) 
    minstret <= minstret + 1'b1;
// FIND:marchid
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    marchid <= `XLEN'h48454c4c4f534a48;
// FIND:misa
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    misa <= `XLEN'h1000000000000100;
// FIND:mstatus
always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    SD   <= `N;  
    FS   <= `ZERO;  
    PRV  <= `PRV_M;    
    MIE  <= `N;  
    MPP  <= `ZERO;  
    MPIE <= `N;
  end else if(wen && (waddr == `CSR_ADDR_mstatus)) begin
    SD   <= wdata[63];
    FS   <= wdata[14:13];
    MPP  <= wdata[12:11];
    MPIE <= wdata[7];
    MIE  <= wdata[3];
  end else if (isEcall || isClint) begin
    MPIE <= MIE;
    MIE  <= `N;
    MPP  <= PRV;
    PRV  <= `PRV_M;
    SD <= (FS == 'd3);
  end else if(isMret)begin
    MIE  <= MPIE;
    MPIE <= `Y;
    PRV  <= MPP;
    MPP  <= `PRV_M;
    SD <= (FS == 'd3);
  end else begin
    SD <= (FS == 'd3);
  end
assign  mstatus = {(FS == 'd3),48'd0,FS,MPP,3'd0,MPIE,3'd0,MIE,3'd0};
// FIND:mip
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    MTIP <= `N;
  else if(wen && (waddr == `CSR_ADDR_mip))begin
    MTIP <= wdata[7];
  end else
    MTIP <= clintInterrupt;
assign mip = {56'd0,MTIP,7'd0};
// FIND:mie
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    MTIE <= `N;
  else if(wen && (waddr == `CSR_ADDR_mie))begin
    MTIE <= wdata[7];
  end
assign mie = {56'd0,MTIE,7'd0};
// FIND:mcause
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mcause <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mcause))begin
    mcause <= wdata;
  end else if(isClint) begin
    mcause <= {1'b1,`Cause_clint};
  end else if (isEcall) begin
    mcause <= {1'b0,`Cause_machine_ecall};
  end
// FIND:mtvec
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mtvec <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mtvec))begin
    mtvec <= wdata;
  end
// FIND:mepc
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    mepc <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mepc))begin
    mepc <= wdata;//[`VADDR_W-1:2];
  end else if(isEcall || isClint) begin
    mepc[`VADDR_W-1:2] <= wbu_csr_pc_i[`VADDR_W-1:2];
  end
// FIND:mscratch
always@(posedge clk or negedge rst_n)
  if(~rst_n)  
    mscratch <= `ZERO;
  else if(wen && (waddr == `CSR_ADDR_mscratch))begin
    mscratch <= wdata;    
  end
// read logical 
reg  [`XLEN-1:0] out;
always@(*)begin
  case(raddr)
    `CSR_ADDR_mstatus  : out = mstatus  ;
    `CSR_ADDR_misa     : out = misa     ;
    `CSR_ADDR_medeleg  : out = medeleg  ;
    `CSR_ADDR_mideleg  : out = mideleg  ;
    `CSR_ADDR_mie      : out = mie      ;
    `CSR_ADDR_mtvec    : out = mtvec    ;
    `CSR_ADDR_mscratch : out = mscratch ;
    `CSR_ADDR_mepc     : out = mepc     ;
    `CSR_ADDR_mcause   : out = mcause   ;
    `CSR_ADDR_mtval    : out = mtval    ;
    `CSR_ADDR_mip      : out = mip      ;
    `CSR_ADDR_mcycle   : out = mcycle   ;
    `CSR_ADDR_minstret : out = minstret ;
    `CSR_ADDR_marchid  : out = marchid  ;
    `CSR_ADDR_mimpid   : out = mimpid   ;
    default:out = `ZERO;
  endcase
end
assign rdata = out;

`ifdef DIFFTEST
reg [31:0]intrNOR ;
reg [`VADDR_W-1:0] exPC;
reg [31:0] exInst;

always@(posedge clk or negedge rst_n)
  if(~rst_n)begin
    intrNOR <= `ZERO;
    exPC    <= `ZERO;
    exInst  <= `ZERO;
  end else begin
    intrNOR <= isClint ? mcause[31:0] : `ZERO;
    exPC    <= wbu_csr_pc_i  ;
    exInst  <= wbu_csr_inst_i;
  end
reg t;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    t <= `N;
  else 
    t <= isClint;
DifftestArchEvent DifftestArchEvent(
  .clock (clk),
  .coreid('d0),
  .intrNO (t ? mcause[31:0] : `ZERO),
  .cause(`ZERO),
  .exceptionPC   ( exPC    ),
  .exceptionInst ( exInst  )
);

DifftestCSRState DifftestCSRState(
  .clock              (clk),
  .coreid             (0),
  .priviledgeMode     ('d3),
  .mstatus            (mstatus),
  .sstatus            (mstatus & 64'h80000003000DE122),
  .mepc               (mepc),
  .sepc               (0),
  .mtval              (mtval),
  .stval              (0),
  .mtvec              (mtvec),
  .stvec              (0),
  .mcause             (mcause),
  .scause             (0),
  .satp               (0),
  .mip                (0),
  .mie                (mie),
  .mscratch           (mscratch),
  .sscratch           (0),
  .mideleg            (0),
  .medeleg            (0)
);
`endif
endmodule